Explore data

eda
Author

Renato Hermoza

Published

September 30, 2022

Get data

Last execution time: 24/02/2025 05:44:27
Products type filter
explore_types = ['frutas', 'lacteos', 'verduras', 'embutidos', 'panaderia', 'desayuno', 'congelados', 'abarrotes',
                 'aves', 'carnes', 'pescados']
Data table
path = Path('../../output')
csv_files = L(path.glob('*.csv')).filter(lambda o: os.stat(o).st_size>0)
pat_store = re.compile('(.+)\_\d+')
pat_date = re.compile('.+\_(\d+)')
df = (
    pd.concat([pd.read_csv(o).assign(store=pat_store.match(o.stem)[1], date=pat_date.match(o.stem)[1])
               for o in csv_files], ignore_index=True)
    .pipe(lambda d: d.assign(
        name=d.name.str.lower()+' ('+d.store+')',
        sku=d.id.where(d.sku.isna(), d.sku).astype(int),
        date=pd.to_datetime(d.date)
    ))
    .drop('id', axis=1)
    .loc[lambda d: d.category.str.contains('|'.join(explore_types))]
    # Filter products with recent data
#     .loc[lambda d: d.name.isin(d.groupby('name').date.max().loc[ge(datetime.now()-timedelta(days=30))].index)]
    # Filter empty prices
    .loc[lambda d: d.price>0]
)
print(df.shape)
df.sample(3)
(1272671, 8)
sku name brand category uri price store date
2205596 50090 ají amarillo molido piki doypack 1kg (plaza_vea) PIKI https://www.plazavea.com.pe/abarrotes https://www.plazavea.com.pe/aji-amarillo-molid... 20.40 plaza_vea 2023-01-23
1234082 28404 filete de salmón fresco importado (plaza_vea) PLAZA VEA https://www.plazavea.com.pe/carnes-aves-y-pesc... https://www.plazavea.com.pe/filete-de-salmon-f... 78.90 plaza_vea 2023-09-02
518887 81715 pack b&d ketchup frasco 245g + mostaza frasco ... B&D https://www.plazavea.com.pe/abarrotes https://www.plazavea.com.pe/pack-b-d-ketchup-f... 6.99 plaza_vea 2023-02-23

Top changes (ratio)

Code
top_changes = (df
 # Use last 30 days of data to compare prices
 .loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
 .sort_values('date')
 # Get percentage change
 .assign(change=lambda d: d
     .groupby(['store','sku'], as_index=False)
     .price.transform(lambda d: (d-d.shift())/d.shift())
 )
 .groupby(['store','sku'], as_index=False)
 .agg({'price':'last', 'change':'mean', 'date':'last'})
 .rename({'price':'last_price', 'date':'last_date'}, axis=1)
 .dropna()
 .loc[lambda d: d.last_date==d.last_date.max()]
 .loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes.head(3)
store sku last_price change last_date
5623 plaza_vea 11577779 8.3 0.327381 2025-02-24
5625 plaza_vea 11577781 8.3 0.327381 2025-02-24
5626 plaza_vea 11577782 8.3 0.327381 2025-02-24
Code
def plot_changes(df_changes, title):
    selection = alt.selection_point(fields=['name'], bind='legend')
    dff = df_changes.drop('change', axis=1).merge(df, on=['store','sku'])
    return (dff
     .pipe(alt.Chart)
     .mark_line(point=True)
     .encode(
         x='date',
         y='price',
         color=alt.Color('name').scale(domain=sorted(dff.name.unique().tolist())),
         tooltip=['name','price','last_price']
     )
     .add_params(selection)
     .transform_filter(selection)
     .interactive()
     .properties(width=650, title=title)
     .configure_legend(orient='top', columns=3)
    )
Code
top_changes.head(10).pipe(plot_changes, 'Top changes')
Code
(top_changes
 .sort_values('change')
 .head(10)
 .pipe(plot_changes, 'Top drops')
)
Code
(top_changes
 .sort_values('change')
 .tail(10)
 .pipe(plot_changes, 'Top increases')
)

Top changes (absolute values)

Code
top_changes_abs = (df
 # Use last 30 days of data to compare prices
 .loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
 .sort_values('date')
 # Get percentage change
 .assign(change=lambda d: d
     .groupby(['store','sku'], as_index=False)
     .price.transform(lambda d: (d-d.shift()).iloc[-1])
 )
 .groupby(['store','sku'], as_index=False)
 .agg({'price':'last', 'change':'mean', 'date':'last'})
 .rename({'price':'last_price', 'date':'last_date'}, axis=1)
 .dropna()
 .loc[lambda d: d.last_date==d.last_date.max()]
 .loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes_abs.head(3)
store sku last_price change last_date
2979 plaza_vea 10151253 40.9 18.4 2025-02-24
4243 plaza_vea 11063723 48.9 13.0 2025-02-24
1149 plaza_vea 9770 60.9 12.2 2025-02-24
Code
top_changes_abs.head(10).pipe(plot_changes, 'Top changes')
Code
(top_changes_abs
 .sort_values('change')
 .head(10)
 .pipe(plot_changes, 'Top drops')
)
Code
(top_changes_abs
 .sort_values('change')
 .tail(10)
 .pipe(plot_changes, 'Top increases')
)

Search specific products

Code
(df
 .loc[df.name.isin(names)]
 .pipe(alt.Chart)
 .mark_line(point=True)
 .encode(x='date', y='price', color='name', tooltip=['name','price'])
 .properties(width=650, title='Pollo')
 .interactive()
 .configure_legend(orient='top', columns=3)
)
Code
(df
 .loc[df.name.isin(names)]
 .pipe(alt.Chart)
 .mark_line(point=True)
 .encode(x='date', y='price', color='name', tooltip=['name','price'])
 .properties(width=650, title='Palta')
 .interactive()
 .configure_legend(orient='top', columns=3)
)
Code
(df
 .loc[df.name.isin(names)]
 .pipe(alt.Chart)
 .mark_line(point=True)
 .encode(x='date', y='price', color='name', tooltip=['name','price'])
 .properties(width=650, title='Aceite')
 .interactive()
 .configure_legend(orient='top', columns=3)
)
Code
(df
 .loc[df.name.isin(names)]
 .pipe(alt.Chart)
 .mark_line(point=True)
 .encode(x='date', y='price', color='name', tooltip=['name','price'])
 .properties(width=650, title='Aceite')
 .interactive()
 .configure_legend(orient='top', columns=3)
)
Code
(df
 .loc[df.name.isin(names)]
 .pipe(alt.Chart)
 .mark_line(point=True)
 .encode(x='date', y='price', color='name', tooltip=['name','price'])
 .properties(width=650, title='Aceite')
 .interactive()
 .configure_legend(orient='top', columns=3)
)